經過前四天的努力,我們已經成功打造出一個可以視覺化呈現 Google Apps Script 學習地圖的平台了!
從第一天提出 GASO 的概念,到第二天決定使用 Graphviz 來繪製地圖,再到第三天建立 Google Sheets 作為資料後台,最後第四天調整出多種美觀的排版效果。
現在這個地圖看起來已經很漂亮了,但還有一個重要的問題:
它只能看,不能互動!
就像你拿到一張精美的世界地圖,但卻不能點擊任何城市來了解當地的詳細資訊一樣,總覺得少了點什麼。
所以今天,我們要讓這個學習地圖「活」起來!
在我想像中的 GASO 平台,使用者應該要能夠:
這樣一來,學習者就可以主動探索,而不是被動地觀看。
讓我先分析一下我們需要實現哪些互動功能:
現在來看看我們要怎麼實現這些功能。
首先,我們需要為 SVG 中的每個節點添加點擊事件:
function addNodeClickEvents(svg) {
const nodes = svg.querySelectorAll('g.node');
nodes.forEach(node => {
// 讓游標變成手指形狀
node.style.cursor = 'pointer';
// 添加點擊事件
node.addEventListener('click', handleNodeClick);
// 添加 hover 效果
node.addEventListener('mouseenter', function() {
this.style.opacity = '0.8';
});
node.addEventListener('mouseleave', function() {
this.style.opacity = '1';
});
});
}
接下來,我們需要建立節點 ID 與實際資料的對應關係:
function handleNodeClick(event) {
// 取得被點擊的節點 ID
const nodeId = event.currentTarget.id;
// 從全域資料中找到對應的節點資訊
const nodeData = findNodeData(nodeId);
if (nodeData) {
// 顯示節點詳細資訊
showNodeModal(nodeData);
}
}
function findNodeData(nodeId) {
// 從我們之前從 Google Sheets 取得的資料中尋找
return window.graphData.nodes.find(node => node.id === nodeId);
}
我設計了一個美觀的彈出視窗來顯示節點資訊:
function showNodeModal(nodeData) {
// 建立 Modal HTML
const modalHTML = `
<div id="nodeModal" class="modal">
<div class="modal-content">
<span class="close">×</span>
<h2>${nodeData.label}</h2>
<div class="node-info">
<p><strong>節點 ID:</strong>${nodeData.id}</p>
<p><strong>學習狀態:</strong>${getStatusText(nodeData.status)}</p>
<p><strong>學習提示:</strong></p>
<div class="prompt-box">
<textarea readonly>${nodeData.prompt}</textarea>
<button onclick="copyPrompt()">複製 Prompt</button>
</div>
</div>
</div>
</div>
`;
// 插入到頁面中
document.body.insertAdjacentHTML('beforeend', modalHTML);
// 顯示 Modal
const modal = document.getElementById('nodeModal');
modal.style.display = 'block';
// 添加關閉事件
addModalCloseEvents();
}
為了提供良好的使用者體驗,我添加了多種關閉視窗的方式:
function addModalCloseEvents() {
const modal = document.getElementById('nodeModal');
const closeBtn = modal.querySelector('.close');
// 點擊 X 關閉
closeBtn.addEventListener('click', closeModal);
// 點擊背景關閉
modal.addEventListener('click', function(event) {
if (event.target === modal) {
closeModal();
}
});
// ESC 鍵關閉
document.addEventListener('keydown', function(event) {
if (event.key === 'Escape') {
closeModal();
}
});
}
function closeModal() {
const modal = document.getElementById('nodeModal');
modal.remove();
}
為了支援互動功能,我們需要擴展資料結構。
我在 Google Sheets 的 Node 工作表中新增了一個欄位:
更新了 Google Apps Script 中的 getGraphData()
函數,讓它能夠傳遞完整的節點詳細資訊:
function getGraphData() {
// 從 Google Sheets 讀取節點和邊的數據
const nodes = nodeSheet.getRange(2, 1, nodeSheet.getLastRow() - 1, 5).getValues();
const edges = edgeSheet.getRange(2, 1, edgeSheet.getLastRow() - 1, 2).getValues();
// 構建完整的資料結構
const graphData = {
nodes: nodes.map(([id, label, attr, status, prompt]) => ({
id: id,
label: label,
attr: attr,
status: status,
prompt: prompt || '請告訴我關於 ' + label + ' 的詳細資訊'
})),
edges: edges.map(([src, tgt]) => ({ source: src, target: tgt }))
};
return graphData;
}
在前端,我們需要維護節點狀態和鄰接表:
// 全域變數儲存圖形資料
let graphData = null;
// 載入圖形資料
async function loadGraphData() {
try {
const response = await fetch('/api/graph-data');
graphData = await response.json();
// 渲染圖形
await renderGraph();
// 添加互動功能
addNodeClickEvents(document.querySelector('svg'));
} catch (error) {
console.error('載入圖形資料失敗:', error);
}
}
完成這些功能後,GASO 平台現在具備了完整的互動能力:
透過今天的實作,我們成功將靜態的學習地圖轉化為互動式的學習平台。
這個轉變的意義不僅僅是技術上的進步,更重要的是學習體驗的革新:
addNodeClickEvents(svg)
:為 SVG 節點添加點擊事件handleNodeClick(event)
:處理節點點擊事件showNodeModal(nodeData)
:顯示節點詳細資訊彈窗addModalCloseEvents()
:添加彈窗關閉事件Google Sheets → Google Apps Script → 前端 JavaScript → SVG 渲染 → 事件綁定 → 使用者互動
目前的架構設計考慮了未來擴展的可能性:
這樣的設計讓我們可以輕鬆地添加更多互動功能,比如:
明天見!
如果想要看一些我的鐵人賽花邊心得,
也歡迎追蹤我的 Threads 和 Facebook